home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 076-100 / 092 / less / ch.c < prev    next >
C/C++ Source or Header  |  1995-03-13  |  7KB  |  346 lines

  1. /*
  2.  * Low level character input from the input file.
  3.  * We use these special purpose routines which optimize moving
  4.  * both forward and backward from the current read pointer.
  5.  */
  6.  
  7. #include "less.h"
  8.  
  9. public int file = -1;   /* File descriptor of the input file */
  10.  
  11. /*
  12.  * Pool of buffers holding the most recently used blocks of the input file.
  13.  */
  14. #define BUFSIZ  1024
  15. static struct buf {
  16.     struct buf *next, *prev;
  17.     long block;
  18.     char data[BUFSIZ];
  19. };
  20. static struct buf *bufs = NULL;
  21. public int nbufs;
  22.  
  23. /*
  24.  * The buffer pool is kept as a doubly-linked circular list,
  25.  * in order from most- to least-recently used.
  26.  * The circular list is anchored by buf_anchor.
  27.  */
  28. static struct {
  29.     struct buf *next, *prev;
  30. } buf_anchor;
  31. #define END_OF_CHAIN    ((struct buf *)&buf_anchor)
  32. #define buf_head        buf_anchor.next
  33. #define buf_tail        buf_anchor.prev
  34.  
  35. /*
  36.  * If we fail to allocate enough memory for buffers, we try to limp
  37.  * along with a minimum number of buffers.  
  38.  */
  39. #define DEF_NBUFS       2       /* Minimum number of buffers */
  40.  
  41. extern int clean_data;
  42. extern int ispipe;
  43.  
  44. /*
  45.  * Current position in file.
  46.  * Stored as a block number and an offset into the block.
  47.  */
  48. static long ch_block;
  49. static int ch_offset;
  50.  
  51. /* 
  52.  * Length of file, needed if input is a pipe.
  53.  */
  54. static POSITION ch_fsize;
  55.  
  56. /*
  57.  * Largest block number read if input is standard input (a pipe).
  58.  */
  59. static long last_piped_block;
  60.  
  61. /*
  62.  * Get the character pointed to by the read pointer.
  63.  * ch_get() is a macro which is more efficient to call
  64.  * than fch_get (the function), in the usual case 
  65.  * that the block desired is at the head of the chain.
  66.  */
  67. #define ch_get()   ((buf_head->block == ch_block) ? \
  68.             buf_head->data[ch_offset] : fch_get())
  69.     static int
  70. fch_get()
  71. {
  72.     register struct buf *bp;
  73.     register int n;
  74.     register int end;
  75.     POSITION pos;
  76.  
  77.     /*
  78.      * Look for a buffer holding the desired block.
  79.      */
  80.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  81.         if (bp->block == ch_block)
  82.             goto found;
  83.     /*
  84.      * Block is not in a buffer.  
  85.      * Take the least recently used buffer 
  86.      * and read the desired block into it.
  87.      */
  88.     bp = buf_tail;
  89.     bp->block = ch_block;
  90.     pos = ch_block * BUFSIZ;
  91.     if (ispipe)
  92.     {
  93.         /*
  94.          * The block requested should be one more than
  95.          * the last block read.
  96.          */
  97.         if (ch_block != ++last_piped_block)
  98.         {
  99.             /* This "should not happen". */
  100.             char message[80];
  101.             sprintf(message, "Pipe error: last %ld, want %ld\n",
  102.                 last_piped_block-1, ch_block);
  103.             error(message);
  104.             quit();
  105.         }
  106.     } else
  107.         lseek(file, pos, 0);
  108.  
  109.     /*
  110.      * Read the block.  This may take several reads if the input
  111.      * is coming from standard input, due to the nature of pipes.
  112.      */
  113.     end = 0;
  114.     while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
  115.         if ((end += n) >= BUFSIZ)
  116.             break;
  117.  
  118.     if (n < 0)
  119.     {
  120.         error("read error");
  121.         quit();
  122.     }
  123.  
  124.     /*
  125.      * Set an EOF marker in the buffered data itself.
  126.      * Then ensure the data is "clean": there are no 
  127.      * extra EOF chars in the data and that the "meta"
  128.      * bit (the 0200 bit) is reset in each char.
  129.      */
  130.     if (end < BUFSIZ)
  131.     {
  132.         ch_fsize = pos + end;
  133.         bp->data[end] = EOF;
  134.     }
  135.  
  136.     if (!clean_data)
  137.         while (--end >= 0)
  138.         {
  139.             bp->data[end] &= 0177;
  140.             if (bp->data[end] == EOF)
  141.                 bp->data[end] = '@';
  142.         }
  143.  
  144.     found:
  145.     /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
  146.     {
  147.         /*
  148.          * Move the buffer to the head of the buffer chain.
  149.          * This orders the buffer chain, most- to least-recently used.
  150.          */
  151.         bp->next->prev = bp->prev;
  152.         bp->prev->next = bp->next;
  153.  
  154.         bp->next = buf_head;
  155.         bp->prev = END_OF_CHAIN;
  156.         buf_head->prev = bp;
  157.         buf_head = bp;
  158.     }
  159.     return (bp->data[ch_offset]);
  160. }
  161.  
  162. /*
  163.  * Determine if a specific block is currently in one of the buffers.
  164.  */
  165.     static int
  166. buffered(block)
  167.     long block;
  168. {
  169.     register struct buf *bp;
  170.  
  171.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  172.         if (bp->block == block)
  173.             return (1);
  174.     return (0);
  175. }
  176.  
  177. /*
  178.  * Seek to a specified position in the file.
  179.  * Return 0 if successful, non-zero if can't seek there.
  180.  */
  181.     public int
  182. ch_seek(pos)
  183.     register POSITION pos;
  184. {
  185.     long new_block;
  186.  
  187.     new_block = pos / BUFSIZ;
  188.     if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
  189.     {
  190.         /*
  191.          * Set read pointer.
  192.          */
  193.         ch_block = new_block;
  194.         ch_offset = pos % BUFSIZ;
  195.         return (0);
  196.     }
  197.     return (1);
  198. }
  199.  
  200. /*
  201.  * Seek to the end of the file.
  202.  */
  203.     public int
  204. ch_end_seek()
  205. {
  206.     if (ispipe)
  207.     {
  208.         /*
  209.          * Do it the slow way: read till end of data.
  210.          */
  211.         while (ch_forw_get() != EOF)
  212.             ;
  213.     } else
  214.     {
  215.         (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
  216.     }
  217.     return (0);
  218. }
  219.  
  220. /*
  221.  * Return the length of the file, if known.
  222.  */
  223.     public POSITION
  224. ch_length()
  225. {
  226.     if (ispipe)
  227.         return (ch_fsize);
  228.     return ((POSITION)(lseek(file, (off_t)0, 2)));
  229. }
  230.  
  231. /*
  232.  * Return the current position in the file.
  233.  */
  234.     public POSITION
  235. ch_tell()
  236. {
  237.     return (ch_block * BUFSIZ + ch_offset);
  238. }
  239.  
  240. /*
  241.  * Get the current char and post-increment the read pointer.
  242.  */
  243.     public int
  244. ch_forw_get()
  245. {
  246.     register int c;
  247.  
  248.     c = ch_get();
  249.     if (c != EOF && ++ch_offset >= BUFSIZ)
  250.     {
  251.         ch_offset = 0;
  252.         ch_block ++;
  253.     }
  254.     return (c);
  255. }
  256.  
  257. /*
  258.  * Pre-decrement the read pointer and get the new current char.
  259.  */
  260.     public int
  261. ch_back_get()
  262. {
  263.     register int c;
  264.  
  265.     if (--ch_offset < 0)
  266.     {
  267.         if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  268.         {
  269.             ch_offset = 0;
  270.             return (EOF);
  271.         }
  272.         ch_offset = BUFSIZ - 1;
  273.         ch_block--;
  274.     }
  275.     c = ch_get();
  276.     return (c);
  277. }
  278.  
  279. /*
  280.  * Initialize the buffer pool to all empty.
  281.  * Caller suggests that we use want_nbufs buffers.
  282.  */
  283.     public void
  284. ch_init(want_nbufs)
  285.     int want_nbufs;
  286. {
  287.     register struct buf *bp;
  288.     char *calloc();
  289.  
  290.     if (nbufs < want_nbufs)
  291.     {
  292.         /*
  293.          * We don't have enough buffers.  
  294.          * Free what we have (if any) and allocate some new ones.
  295.          */
  296.         if (bufs != NULL)
  297.             free((char *)bufs);
  298.         bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
  299.         nbufs = want_nbufs;
  300.         if (bufs == NULL)
  301.         {
  302.             /*
  303.              * Couldn't get that many.
  304.              * Try for a small default number of buffers.
  305.              */
  306.             char message[80];
  307.             sprintf(message,
  308.               "Cannot allocate %d buffers.  Using %d buffers.", 
  309.               nbufs, DEF_NBUFS);
  310.             error(message);
  311.             bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
  312.             nbufs = DEF_NBUFS;
  313.             if (bufs == NULL)
  314.             {
  315.                 /*
  316.                  * Couldn't even get the smaller number of bufs.
  317.                  * Something is wrong here, don't continue.
  318.                  */
  319.                 sprintf(message, 
  320.                 "Cannot even allocate %d buffers!  Quitting.\n",
  321.                   DEF_NBUFS);
  322.                 error(message);
  323.                 quit();
  324.                 /*NOTREACHED*/
  325.             }
  326.         }
  327.     }
  328.  
  329.     /*
  330.      * Initialize the buffers to empty.
  331.      * Set up the circular list.
  332.      */
  333.     for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
  334.     {
  335.         bp->next = bp + 1;
  336.         bp->prev = bp - 1;
  337.         bp->block = (long)(-1);
  338.     }
  339.     bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
  340.     buf_head = &bufs[0];
  341.     buf_tail = &bufs[nbufs-1];
  342.     last_piped_block = -1;
  343.     ch_fsize = NULL_POSITION;
  344.     (void) ch_seek((POSITION)0);
  345. }
  346.